package com.lingxu.base.system.common.service.impl;

/**
 * @program: sjmf_java
 * @ClassName SysLogAspect
 * @description:
 * @author: gxy
 * @create: 2023-05-04 17:00
 * @Version 1.0
 **/
import com.alibaba.fastjson.JSON;
import com.lingxu.base.common.util.IPUtils;
import com.lingxu.base.common.util.SpringContextUtils;
import com.lingxu.base.system.common.anno.SysLogAnno;
import com.lingxu.base.system.common.entity.SysLog;
import com.lingxu.base.system.common.service.ISysLogService;
import com.lingxu.base.system.manage.vo.LoginUser;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import java.util.Date;

/**
 * 系统日志切面
 */
@Aspect
@Component
@Slf4j
public class SysLogAspect {

    @Resource
    private ISysLogService iSysLogService;

    /**
     * 这里我们使用注解的形式
     * 当然，我们也可以通过切点表达式直接指定需要拦截的package,需要拦截的class 以及 method
     * 切点表达式:   execution(...)
     *
     * execution(public * *(..)) 任意的公共方法
     * execution（* set*（..）） 以set开头的所有的方法
     * execution（* com.LoggerApply.*（..））com.LoggerApply这个类里的所有的方法
     * execution（* com.annotation.*.*（..））com.annotation包下的所有的类的所有的方法
     * execution（* com.annotation..*.*（..））com.annotation包及子包下所有的类的所有的方法
     * execution(* com.annotation..*.*(String,?,Long)) com.annotation包及子包下所有的类的有三个参数，第一个参数为String类型，第二个参数为任意类型，第三个参数为Long类型的方法
     * execution(@annotation(com.lingyejun.annotation.Lingyejun))
     */
    @Pointcut("@annotation(com.lingxu.base.system.common.anno.SysLogAnno)")
    public void logPointCut() {}

    /**
     * 环绕通知 @Around  ， 当然也可以使用 @Before (前置通知)  @After (后置通知)
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        // 请求的参数
        Object[] args = point.getArgs();
        try {
            log.info("执行：{},请求参数：{}", point.getSignature().getName(), JSON.toJSONString(args));
        } catch (Exception e) {

        }
        Object result;
        String error = null;
        // 执行方法
        try {
            result = point.proceed();
        } catch (Exception e) {
            error = e.getMessage();
            log.info("执行：{} 异常：",point.getSignature().getName(),point.getSignature(),e);
            throw e;
        } finally {
            // 执行时长(毫秒)
            long time = System.currentTimeMillis() - beginTime;
            log.info("执行：{},耗时：{}",point.getSignature().getName(), time);
            //异步保存日志
            saveLog(point, error, time);
        }
        return result;
    }

    void saveLog(ProceedingJoinPoint joinPoint,String error, long time) {
        try {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            SysLogAnno syslog = method.getAnnotation(SysLogAnno.class);
            LoginUser userNow = (LoginUser) SecurityUtils.getSubject().getPrincipal();
            if (null == userNow) {
                userNow = new LoginUser();
            }
            SysLog sysLog = new SysLog();
            HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
            sysLog.setIp(IPUtils.getIpAddr(request));
            sysLog.setUserid(userNow.getId());
            sysLog.setUsername(userNow.getUsername());
            sysLog.setLogtype(syslog.logType().getCode());
            sysLog.setOperatetype(syslog.operateType().getCode());
            sysLog.setCreatetime(new Date());
            sysLog.setCreateby(userNow.getId());
            sysLog.setRealname(userNow.getName());
            String text = "用户：" + userNow.getName() + "," + syslog.desc();
            sysLog.setLogcontent(text);
            sysLog.setExceptioncontent(null!=error&&error.length()>2000?error.substring(0,2000):error);
            iSysLogService.save(sysLog);
        } catch (Exception e) {

        }
    }
}
